﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using WheelOfCivilization.Map.MapObject.Unmovable.Nature;
using WheelOfCivilization.Map.MapObject.Unmovable.Buildings;
using WheelOfCivilization.Map.MapObject.Movable;
using Microsoft.Xna.Framework;
using Microsoft.Xna.Framework.Graphics;
using System.IO;


namespace WheelOfCivilization.Map
{
    public class WorldCluster
    {
        public WorldCluster[] neigbourhood = new WorldCluster[8];               //pole ukazujici na clustery okolo (pro navigaci)
        public int X, Y;                                                        //souradnice (pro navigaci, ukladani a nacitani)
        private List<int[]>[] borders = new List<int[]>[4];                     //pole seznamu poli... hehe, je to jednoduchy, ale vypada to hrozne
                                                                                //int[] kterej vidis vevnitr obsahuje 2 cisla 0-9, ktery urcujou souradnice, kudy se da prejit z clusteru do clusteru (tzn. ani na jednom hranicnim ctverecku nic neni)
                                                                                //ty jsou v seznamu (list). C# je trochu dementni co se tyce poli, pac musis znat jejich velikost a dost debilne se realokujou. List je resenim a jelikoz hranice muze bejt
                                                                                //zaplnena nebo uplne prazdna, je list fajn reseni
                                                                                //no a ty listy jsou 4 - nadrazeny pole ma rozmer 4, jelikoz kazdy hranici pripada jeden list souradnic :)
        
        private WorldObject[,] unmovableContent = new WorldObject[10,10];       //dvourozmerne pole 10*10 obsahujici objekty WorldObject -> tohle je samotna mapa. WorldObject je abstraktni trida, kterou pak dedej kokotiny jako Ground, Gold, Stone, Wood atd. atd.
        private List<MovingObject> movableContent = new List<MovingObject>();   //seznam pohybujicich se objektu, tady budou krome jednotek hrace i zvirata a jednotky ostatnich hracu -> zase list, nevis, kolik jich bude
        private List<BuildingObject> buildings = new List<BuildingObject>();                //list budov postavenych v clusteru
        private List<MovingObject> units = new List<MovingObject>();            //list jednotek hrace v clusteru

        public List<MovingObject> unitsGS                                       //Getter/Setter -> vetsina objektu nahore ^^ ma atribut private - nejde k nim pristupovat odjinud, nez ze tridy WorldCluster - proto jsou tady tyhle "metody", ktery jsou prave pro cteni/psani do tehle objektu (prava budou upravovany podle toho, kdo k nim bude pristupovat a odkud, NEzustane jim atribut public)
        {
            get { return this.units; }
            set { this.units = value; }
        }

        public List<BuildingObject> buildingsGS
        {
            get { return this.buildings; }
            set { this.buildings = value; }
        }

        public List<MovingObject> movableContentGS
        {
            get { return this.movableContent; }
            set { this.movableContent = value; }
        }

        public WorldObject[,] unmovableContentGS
        {
            get { return this.unmovableContent; }
            set { this.unmovableContent = value; }
        }
        
        public List<int[]>[] bordersGS
        {
            get { return this.borders; }
            set { this.borders = value; }
        }

        private static Random rnd = new Random();   //staticka promenna pro generovani nahodnych cisel

        public WorldCluster()           //konstruktor pro prvni cluster
        {
            this.X = 0;                 //souradnice
            this.Y = 0;
            generateFirstCluster();     //metoda plnici prvni cluster (odlisna od jinych -> ostatni clustery nemaji centrum, nejsou v nich inicializovane jednotky a obsahuji vic surovin)
        }
        
        public WorldCluster(int X, int Y)   //konstruktor dalsich clusteru - vidis, ze nazev metody, navratova hodnota atd. jsou stejny, zmena je pouze ve vstupu, tohle umej vsechny objektove orientovany jazyky, rika se tomu pretezovani metod
        {
            this.X = X;
            this.Y = Y;
            generateCluster();
        }

        public WorldCluster(String buildCluster) //konstruktor clusteru z ulozenyho souboru, dost krkolomna konstrukce, ted neresit
        {
            this.borders[0] = new List<int[]>();
            this.borders[1] = new List<int[]>();
            this.borders[2] = new List<int[]>();
            this.borders[3] = new List<int[]>();
            string[] s = new string[] { "\n" };
            string[] result = buildCluster.Split(s, StringSplitOptions.None); 
            //result[0] = <location X, Y>
            //result[1] = <unmovableContent>
            //result[2] -> result[11] = <WorldObject>   <WorldObject>   ...
            //result[12] = </unmovableContent>
            //result[13] = <movableContent>
            //result[14] -> result[n] = <MovingObject, X, Y>
            //result[n+1] = </movableContent>
            //result[n+2] = <buildingd>
            //result[n+3] -> result[m] = <Building, X, Y>
            //result[m+1] = </location>
            string hlp = "<location ";
            string[] buffer = new string[10];
            result[0] = result[0].Trim(hlp.ToCharArray());
            Int32.TryParse(result[0].Split(',')[0], out this.X);
            var grr = result[0].Split(',')[1].TrimEnd(new char[] {'>', '\r'});
            Int32.TryParse(grr, out this.Y);
            if (result[1].TrimStart(' ') == "<unmovableContent>\r")
            {
                hlp = "";
                for (int i = 2; i < 13; i++)
                {
                    if (i == 12)
                        break;
                    buffer = (result[i].Split(hlp.ToCharArray(), StringSplitOptions.None));
                    var v = buffer.AsEnumerable();
                    var w = v.Where(x => x != "");
                    buffer = w.ToArray();
                    for (int j = 0; j < 10; j++)
                    {
                        WorldObject wo = null;
                        switch (buffer[j])
                        {
                            case ("<ground>"):
                                {
                                    wo = new Ground(this);
                                    break;
                                }
                            case ("<wood>"):
                                {
                                    wo = new Wood(this);
                                    break;
                                }
                            case ("<gold>"):
                                {
                                    wo = new Gold(this);
                                    break;
                                }
                            case ("<stone>"):
                                {
                                    wo = new Stone(this);
                                    break;
                                }
                            case ("<center>"):
                                {
                                    wo = new Center(this);
                                    break;
                                }
                        }
                        this.unmovableContent[i - 2, j] = wo;
                    }
                }
                int row = 13;
                if (result[row].TrimStart(' ') == "<movableContent>\r")
                {
                    while (true)
                    {
                        row++;
                        if (result[row].TrimStart(' ') == "</movableContent>\r")
                            break;
                        MovingObject mo = null;
                        var tmp = result[row].Split(',');
                        tmp[0] = tmp[0].TrimStart(new char[] {' ', '<'});
                        tmp[2] = tmp[2].TrimEnd(new char[]{'>', '\r'});
                        switch(tmp[0])
                        {
                            case("villager"):
                                {
                                    mo = new Villager();
                                    break;
                                }
                        }
                        mo.inCluster = this;
                        mo.pos = new Point(Int32.Parse(tmp[1]), Int32.Parse(tmp[2]));
                        this.units.Add(mo);
                        this.movableContent.Add(mo);
                    }
                    row++;
                }
                if (result[row].TrimStart(' ') == "<buildings>\r")
                {
                    while (true)
                    {
                        row++;
                        if (result[row].TrimStart(' ') == "</buildings>\r")
                            break;
                        BuildingObject b = null;
                        var tmp = result[row].Split(',');
                        tmp[0] = tmp[0].TrimStart(new char[] { ' ', '<' });
                        tmp[2] = tmp[2].TrimEnd(new char[] { '>', '\r' });
                        switch (tmp[0])
                        {
                            case ("center"):
                                {
                                    b = new Center(this);
                                    break;
                                }
                        }
                        b.position = new Point(Int32.Parse(tmp[1]), Int32.Parse(tmp[2]));
                        this.buildings.Add(b);
                    }
                    row++;
                }
                if (result[row] != "</location>\r")
                    throw new Exception("Nejsme na konci dokumentu!");
            }
        }

        private void generateFirstCluster()
        {
            for (int i = 0; i < 8; i++)
                this.neigbourhood[i] = null;

            this.borders[0] = new List<int[]>();
            this.borders[1] = new List<int[]>();
            this.borders[2] = new List<int[]>();
            this.borders[3] = new List<int[]>();

            
            for (int x = 0; x < 10; x++)
            {
                for (int y = 0; y < 10; y++)
                {
                    Ground gr = new Ground(this);
                    gr.visible = true;
                    gr.position = new Point(x, y);
                    unmovableContent[x, y] = gr;  //tady ty dva for cykly plnej pole unmovableContent objektem Ground
                }
            }

            Center mainCenter = new Center(this);   //zavadeni instance Center
            mainCenter.size = new byte[2] { 4, 4 }; //vse co dedi tridu building musi mit velikost
            mainCenter.position = new Point(3, 3);  //a pozici
            mainCenter.visible = true;
            mainCenter.fogged = false;
            buildings.Add(mainCenter);

            for (int x = 3; x < 7; x++)
            {
                for (int y = 3; y < 7; y++)
                {
                    this.unmovableContent[x, y] = mainCenter; //a na mape nastavim, kde se nachazi
                                                              //vzhledem k tomu, ze jde o first cluster, muze to bejt napsany takhle "natvrdo" - stejne by slo pouzit neco ve smyslu:
                    //for(x = center.position.X; x < (center.position.X + center.size.X); x++) { for(y = center.position.Y; y < (center.position.Y + center.size.Y); y++)
                }
            }

            byte count = 1;//(byte)rnd.Next(0, 10);     //random promenna, tkera urci pocet stromu v prvni clusteru
            int vill = 1;                           //a tri villageri
            while (true)                            //dokud neumistim stromy a villagery, projizdim furt dokola (chci random pozice)
            {
                byte x, y;
                x = (byte)rnd.Next(0, 9);
                y = (byte)rnd.Next(0, 9);
                if (!this.unmovableContent[x, y].occupied)  //pokud je pozice volna
                {
                    if (vill > 0)                           //pokud budu pridavat villagera
                    {
                        Villager v = new Villager();
                        v.pos = new Point(x, y);
                        v.inCluster = this;
                        v.moving = false;
                        v.visible = true;
                        movableContent.Add(v);
                        units.Add(v);
                        vill--;
                    }
                    else                                    //pokud uz nepridavam villagery, zacnu pridavat stromy
                    {
                        if (movableContent.Where(w => w.pos.X == x && w.pos.Y == y).Count() < 1)    //pokud je pozice volna && na pozici nestoji villager
                        {
                            Wood t = new Wood(this);
                            t.position = new Point(x, y);
                            t.visible = true;
                            t.fogged = false;
                            t.value = 100;
                            unmovableContent[x, y] = t;
                            count--;
                            if (count < 1 || count > 20)    //tady ta podminka je ochrana proti preteceni, pac kdyz byte pretece, nastavi se na 255 a 255 stromu se do pole 10*10 fakt nevejde
                                break;
                        }
                    }

                }
            }
        }

        private void generateCluster()
        {
            this.borders[0] = new List<int[]>();
            this.borders[1] = new List<int[]>();
            this.borders[2] = new List<int[]>();
            this.borders[3] = new List<int[]>();
            byte s = (byte)Program.objectProbability.stone;     //ulozim si promenny, podle kterejch generuju objekty do mapy
            byte g = (byte)Program.objectProbability.gold;
            byte w = (byte)Program.objectProbability.wood;
            byte tmp;
            NatureObject localWO = null;     //deklaruju si promennou typu WorldObject, kterou pak pouziju k plneni unmovableContent[,]

            for (int x = 0; x < 10; x++)    //pro kazdy pole unmovableContent[,]
            {
                for (int y = 0; y < 10; y++)
                {
                    tmp = (byte)rnd.Next(Environment.TickCount);    //vygeneruju nahodny cislo
                    tmp = (byte)(tmp % 100);                        //a vezmu zbytek po deleni stem -> mam cislo 0-100
                    if (tmp > w)
                    {
                        localWO = new Ground(this);                 //pokud je cislo v intervalu (w, 100) -> na [x,y] je hola zem
                    }
                    else
                    {
                        if (tmp > g)                                //pokud je cislo v intervalu (g, w) -> drevo
                        {
                            localWO = new Wood(this);
                            localWO.value = 100;
                            w += 6;                                 //zvysim pravdepodobnost dreva v clusteru (tohle budu jeste jinak, chtel bych, aby se suroviny grupovaly na jedno misto a takhle to plni cluster hlavne doprava dolu, coz je trochu blbost, hehe...
                        }
                        else
                        {
                            if (tmp > s)
                            {
                                localWO = new Gold(this);
                                localWO.value = 500;
                                g += 2;
                            }
                            else
                            {
                                localWO = new Stone(this);
                                localWO.value = 250;
                                s += 1;
                            }
                        }
                    }
                    localWO.visible = false;
                    localWO.fogged = true;
                    localWO.position = new Point(x, y);
                    unmovableContent[x, y] = localWO;
                }
            }


        }
    }
}
